package com.itany.shop.util;

import com.itany.shop.exception.DataAccessException;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Miss Chen
 * @version 1.0
 * @date 2024年08月14日13:34
 */
public class JDBCTemplate<T> {
    /**
     * 执行一个DML操作，即:insert、delete、update
     * 对象增删改，使用ps.excuteUpdate执行sql
     * 将不一样的sql语句作为参数
     * 由于sql语句的参数数量不确定的，因此，可以使用可变长参数进行封装
     * @param sql
     * @param params
     */
    public void update(String sql,Object... params){

        Connection conn=null;
        PreparedStatement ps=null;

        try {
            conn=JDBCUtil.getConnection();
            ps=conn.prepareStatement(sql);

            //遍历可变长参数
            for(int i=0;i<params.length;i++){
                Object param=params[i];
                if(param instanceof String){
                    ps.setString(i+1,(String)param);
                }else if(param instanceof Integer){
                    ps.setInt(i+1,(Integer) param);
                }else if(param instanceof Double){
                    ps.setDouble(i+1,(Double)param);
                }else{
                    ps.setObject(i+1,param);
                }
            }
            //执行DML操作
            ps.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
            throw new DataAccessException("数据库访问异常");
        }finally {
            //在业务层的事务操作中需要使用Connection
            // 因此，此时释放资源时不要释放Connection
            JDBCUtil.close(null,ps,null);
        }
    }

    /**
     * 执行查询操作
     * 查询操作会返回一个结果，其结果可能是一条，也可能是多条
     * 因此，可以通过集合来接收返回的结果
     * 对于查询结果，我们需要将返回的结果映射成Java对象
     * 具体映射成哪个对象，目前不确定，只有实际调用时候才能确定
     * 因此，通过接口作为模板方法的参数，调用者在实际调用的时候做具体的实现
     * @param sql
     * @param rm
     * @param params
     * @return
     */
    public List<T> query(String sql,RowMapper rm,Object... params){
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        List<T> list=new ArrayList<>();

        try {
            conn=JDBCUtil.getConnection();
            ps=conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                Object param=params[i];
                if(param instanceof String){
                    ps.setString(i+1,(String)param);
                }else if(param instanceof Integer){
                    ps.setInt(i+1,(Integer) param);
                }else if(param instanceof Double){
                    ps.setDouble(i+1,(Double)param);
                }else{
                    ps.setObject(i+1,param);
                }
            }
            //执行查询操作
            rs=ps.executeQuery();
            while(rs.next()){
                T t= (T) rm.mapRow(rs);
                list.add(t);
            }
            return list;

        } catch (Exception e) {
            e.printStackTrace();
            throw new DataAccessException("数据访问异常");
        }finally {
            JDBCUtil.close(null,ps,rs);
        }
    }

    /**
     * 执行一个DML操作，用于保存数据的同时返回生成的主键值
     * 查询
     * @param psc
     * @param keyHolder
     */
    public void insertReturnPrimaryKey(PreparedStatementCreator psc,KeyHolder keyHolder){
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;

        try {
            conn=JDBCUtil.getConnection();

            //调用者在调用方法时候做具体实现
            // 在实现的时候，会提供构建的状态集所需要的sql与参数
            ps=psc.createPreparedStatement(conn);
            ps.executeUpdate();

            //保存后数据库中自动生成对应的主键
            rs=ps.getGeneratedKeys();

            //准备一个集合，用于存储所有自动生成的主键值
            List<Integer> list=new ArrayList<>();

            //通过结果集元数据获取生成的主键值
            ResultSetMetaData rsmd=rs.getMetaData();

            //获取生成的列的总数
            int columnCount=rsmd.getColumnCount();
            if(rs.next()){
                //按行读取数据的时候，遍历所有列的值
                // 将每一列的值存放到对应的集合中
                for(int i=1;i<=columnCount;i++){
                    list.add(rs.getInt(i));
                }

            }

            keyHolder.setKeyList(list);


        } catch (Exception e) {
            throw new DataAccessException("数据库访问异常");
        }finally {
            JDBCUtil.close(null,ps,rs);
        }
    }

}
